home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Adobe Graphics & Publishing SDK 1996 December
/
Adobe Graphics & Publishing SDK 1996 December.iso
/
mac
/
Premiere 4.2 SDK r3 Mac
/
Examples
/
Projects
/
Storyboard
/
Storyboard.c
< prev
next >
Wrap
Text File
|
1996-01-25
|
31KB
|
887 lines
//========================================================================================
//
// Storyboard.c - Export marked frames as one or more storyboard PICT files.
//
// Written by Randy Ubillos and Bryan K. ╥Beaker╙ Ressler.
//
// Copyright ⌐ 1993-96, Adobe Systems Incorporated, all rights reserved worldwide.
//
// Version 1.00 10/20/93 Original version.
// Version 1.01 9/12/94 Updated for 4.0.
// Version 1.02 10/8/95 Updated for 4.2 and CW7.
//
//========================================================================================
//========================================================================================
// Includes - use precompiled headers if compiling with CodeWarrior.
//========================================================================================
#ifdef __MWERKS__
#ifdef powerc
#include "PremierePPC"
#else
#include "Premiere68k"
#endif
#else
#include "Premiere.h"
#endif
//========================================================================================
// Types
//========================================================================================
typedef struct {
short hSize; // Horizontal image size in pixels
short vSize; // Vertical image size in pixels
short rows; // Number of rows vertically
short columns; // Number of columns horizontally
short spacing; // Spacing in pixels
} SBSettingsRec, *SBSettingsPtr, **SBSettingsHdl;
typedef struct {
SBSettingsRec settings; // Our current settings
Rect vidRect; // The frame rectangle
GWorldPtr offWorld; // Offworld only allocated during settings dialog
short ref; // The reference number of the file we're writing to
short error; // The last error code, if non-zero
PicHandle thePic; // The picture we're currently collecting
long count; // The running byte count for this picture
long writeUsed; // The number of bytes used in the write buffer
Ptr writeBuf; // The write buffer itself
} LocalRec;
//========================================================================================
// Constants
//========================================================================================
enum { // Resource IDs
srStrings = 2000, // STR# - general string list
srDialog = 2000, // DLOG - settings dialog
srExtension = 2100, // TEXT - expansion template for numbered file extension
srGray = 2000 // PAT - 50% gray pattern
};
enum { // Items in our settings dialog
iOK = 1,
iCancel,
iLoad,
iSave,
iHorizSize, // 5
iVertSize,
iRowSlider,
iRowNumber,
iColumnSlider,
iColumnNumber, // 10
iSpacingSlider,
iSpacingNumber,
iExample,
iImageSizeTitle,
iImageSizeGroup // 15
};
enum { // Items in our string list
ssPICTPrompt = 1, // Prompt for PICT file name
ssPICTDefaultName, // Default name for PICT file
ssSetPrompt, // Prompt for settings file name
ssSetDefaultName, // Default name for settings file
ssError // An error occurred
};
enum { // Other constants
kSBSetType = 'SBsá', // File type of the storyboard settings file
kWriteBufSize = 32768, // The size of our write-ahead buffer
kFlushBufCmd = -1, // The magic byteSize that says to flush the buffer
kRedraw = true, // For SetToDialog
kNoRedraw = false,
kMinHSize = 100, // Min/max output picture sizes
kMaxHSize = 4000,
kMinVSize = 100,
kMaxVSize = 4000
};
//========================================================================================
// Static prototypes
//========================================================================================
static void FileNameForPage(StringPtr inName, StringPtr outName, short which);
static OSErr WriteZeros(short ref, long numBytes);
static pascal void PutPICTData(char *buffer, short byteCount);
static void InitSettingsRec(SBSettingsPtr settings);
static void SetToDialog(DialogPtr settingsDialog, SBSettingsPtr set, Boolean redraw);
static void DialogToSet(DialogPtr settingsDialog, SBSettingsPtr set);
static Boolean LoadSettings(DialogPtr settingsDialog);
static void SaveSettings(DialogPtr settingsDialog);
static Boolean SettingsDialog(SBSettingsPtr settings, LocalRec *theData);
static void CalcSizes(Rect *srcbox, Rect *dstbox, short rows, short columns,
short spacing, short *hOffset, short *width, short *hSpacing, short *vOffset,
short *height, short *vSpacing);
static pascal void DrawExample(WindowPtr theWindow, short item);
static pascal void DrawNumber(WindowPtr theWindow, short item);
static pascal void ColumnSliderAction(void);
static pascal void RowSliderAction(void);
static pascal void SpacingSliderAction(void);
//========================================================================================
// Export one or more storyboard images
//========================================================================================
pascal short main (short selector, DataExportHandle theData)
{
Str63 prompt, defaultName;
FSSpec curSpec, inSpec;
LocalRec localData;
CGrafPort localPort;
StandardFileReply reply;
CQDProcs localProcs;
QDPutPicUPP putProc;
Rect pageRect, vidBox, destBox;
PicHandle thePic;
GDHandle oldGD;
GWorldPtr theWorld;
GWorldPtr oldGW;
long writeCount, zero = 0, marker, *marks;
short result = 0, i, imageNum, total, count, imagesOnPage, err = 0;
short pageNum, hOffset, width, hSpacing, vOffset, height, vSpacing;
short clipID, fps, numMarks;
Boolean opened = false;
// Act according to the selector
putProc = NewQDPutPicProc(PutPICTData);
if (selector == edExecute) {
GetExportFSSpec(theData, &inSpec);
PrDebug(0, (char*)"\pFSSpec is %s", inSpec.name);/*DJW*/
// Save the current GWorld/GDevice. Clear localData.
GetGWorld(&oldGW, &oldGD);
FillMem((Ptr)&localData, sizeof(LocalRec), 0x00);
// Cache the frame size and initialize a settings record.
localData.vidRect = (*theData)->bounds;
InitSettingsRec(&localData.settings);
// Allocate write-ahead buffer and initialize the used offset
localData.writeBuf = NewPtr(kWriteBufSize);
if (localData.writeBuf == nil)
return(1);
localData.writeUsed = 0;
// Save off the address of localData so that our userItems and put-PICT proc can
// find them.
SetAGlobal(gExportRef, (long)&localData);
// Let the user edit the settings
if (!SettingsDialog(&localData.settings, &localData))
return(0);
UpdateAllWindows();
// Put up a standard put file dialog. This gives us a base picture file name.
GetIndString(prompt, srStrings, ssPICTPrompt);
GetIndString(defaultName, srStrings, ssPICTDefaultName);
StandardPutFile(prompt, defaultName, &reply);
// If they didn't cancel the standard put file dialog, export the images.
if (reply.sfGood) {
// Cache up some information and calculate the number of frames per page.
UpdateAllWindows();
SpinCurs(10);
vidBox = (*theData)->bounds;
SetRect(&pageRect, 0, 0, localData.settings.hSize, localData.settings.vSize);
total = localData.settings.columns * localData.settings.rows;
// Calculate given the input vidBox, the picture rectangle, and the user's
// specified rows, columns, and spacing, calculate the scaled size, offsets,
// and spacing.
CalcSizes(&vidBox, &pageRect, localData.settings.rows,
localData.settings.columns, localData.settings.spacing, &hOffset, &width,
&hSpacing, &vOffset, &height, &vSpacing);
// Retrieve the clip ID and calculate an fps value
clipID = GetExportClipID(theData);
fps = GetExportFPS(theData);
// Find all of the markers, including unnumbered markers. Markers 0 and 1 are
// the in- and out-points, respectively. Manually add the out-point to the end
// of the list (it will always be >= all other markers).
numMarks = CountClipMarkers(clipID);
count = 1;
marks = (long *)NewPtr(numMarks * sizeof(long));
if (marks == nil)
return(1);
marks[0] = (*theData)->markers[0];
for (i = 2; i < numMarks; i++) {
marker = GetClipMarker(clipID, i, fps);
if (marker != -1)
marks[count++] = marker;
}
marks[count++] = (*theData)->markers[1];
// Now that we know how many images we'll be needing to output, figure out how
// many pages that'll take. If it's more than one, create an appropriate file
// name for the first page (if only one page is required, we'll just leave the
// name as the user typed it).
imageNum = 0;
imagesOnPage = 0;
pageNum = 0;
if (count > total)
FileNameForPage(reply.sfFile.name, curSpec.name, pageNum);
else BlockMove(reply.sfFile.name, curSpec.name, reply.sfFile.name[0] + 1);
curSpec.vRefNum = reply.sfFile.vRefNum;
curSpec.parID = reply.sfFile.parID;
// Make a frame-sized GWorld into which we'll get each frame. If successful,
// lock it.
err = SafeNewGWorld(&theWorld, 32, &vidBox, nil, nil, keepLocal);
if (!err)
LockPixels(theWorld->portPixMap);
// Now loop for all images, breaking the list up into pages as we go.
while (!err && (imageNum < count)) {
// Delete the destination file, if it exists. Ignore the error. Create
// the destination PICT file, then open it.
FSpDelete(&curSpec);
err = FSpCreate(&curSpec, 'PrMr', 'PICT', reply.sfScript);
if (!err) {
err = FSpOpenDF(&curSpec, fsWrPerm, &localData.ref);
if (err == noErr)
opened = true;
}
if (!err) {
// Now open a port into which we'll draw our frame images. Since the
// output PICT might be too large to fit in memory (they'll be high-
// resolution if scaled down), install a disk-based put-PICT procedure
// into our port's putPicProc QD bottleneck.
OpenCPort(&localPort);
SetPort((GrafPtr)&localPort);
ClipRect(&pageRect);
SetStdCProcs(&localProcs);
localProcs.putPicProc = putProc;
localData.thePic = nil;
localData.error = 0;
localPort.grafProcs = &localProcs;
// Write the PICT file header (512 bytes of zero).
err = WriteZeros(localData.ref, 512);
if (!err) {
// Write an empty picture header. Later we'll have to re-write
// this little section to update the picture size.
WriteZeros(localData.ref, sizeof(Picture));
if (!err) {
// Open the picture and stuff the relevant information into
// localData so the putPicProc can see it. Set up the initial
// picture size to be the size of a Picture structure. The
// putPicProc will add to this.
thePic = OpenPicture(&pageRect);
localData.thePic = thePic;
localData.count = sizeof(Picture);
// Erase then put the images on the page
EraseRect(&pageRect);
imagesOnPage = 0;
while (imagesOnPage < total && imageNum < count) {
// Calculate the location on the page
destBox.left = hOffset +
(imagesOnPage % localData.settings.columns) *
(width + hSpacing);
destBox.top = vOffset +
(imagesOnPage / localData.settings.columns) *
(height + vSpacing);
destBox.right = destBox.left + width;
destBox.bottom = destBox.top + height;
SetGWorld(theWorld, nil);
EraseRect(&vidBox);
// Use the Premiere getVideo callback to fill our GWorld
// with the appropriate frame, then put it into our output
// picture with a CopyBits call.
((*theData)->getVideo)(marks[imageNum], theWorld,
&vidBox, (*theData)->privateData);
SetPort((GrafPtr)&localPort);
CopyBits((BitMap*)&theWorld->portPixMap,
(BitMap*)&localPort.portPixMap,
&theWorld->portRect, &destBox, ditherCopy, nil);
// Bump the counters of total output images and images
// on this page.
imageNum++;
imagesOnPage++;
}
// Now we're done with one picture image. Close it.
ClosePicture();
// Force-flush our writeahead buffer
PutPICTData(nil, kFlushBufCmd);
}
// If an error occurred, store it so our putPicProc will at least
// have a chance to TRY to put the picture to rest.
if (!err)
err = localData.error;
// Now that we're done, back up to the picture header and write a
// REAL picture header there.
if (!err) {
SetFPos(localData.ref, fsFromStart, 512);
writeCount = sizeof(Picture);
err = FSWrite(localData.ref, &writeCount, (Ptr)*thePic);
}
}
// We're done with the port this time around, so close it.
CloseCPort(&localPort);
}
// Close this picture file. Bump fileName by one (this won't matter
// if there's only one output file).
if (opened)
FSClose(localData.ref);
if (!err)
FileNameForPage(reply.sfFile.name, curSpec.name, ++pageNum);
}
DisposePtr((Ptr)marks);
StopCurs();
}
// Restore the GWorld/GDevice
SetGWorld(oldGW, oldGD);
// If there was an error, nuke the last output file.
if (err) {
FSpDelete(&curSpec);
AlertSystem(0, false, srStrings, ssError, 0, 0);
}
// Restore the GWorld/GDevice
SetGWorld(oldGW, oldGD);
}
DisposeRoutineDescriptor(putProc);
return(result);
}
//========================================================================================
// Build a numbered output file name in an internationally cool way. The parameter inName
// is the "base" part of the file name, outName is a pointer to the output buffer, and
// which is the file number.
//========================================================================================
static void FileNameForPage(StringPtr inName, StringPtr outName, short which)
{
Str255 extStr;
Handle extH;
short extLen;
// Copy the inName to the outName
BlockMove(inName, outName, *inName + 1);
// Expand the numbered extension using an expansion template, then copy the results
// into the Pascal string extStr.
extH = BuildString(srExtension, nil, which);
extLen = GetHandleSize(extH);
if (extLen > 254) extLen = 254;
BlockMove(*extH, extStr + 1, extLen);
extStr[0] = extLen;
DisposeHandle(extH);
// Now that we know how many characters the numbered extension will take, truncate
// the input name if necessary.
if (*outName + extLen > 31)
*outName = 31 - extLen;
// Now put the extension and the input name together
Append(outName, extStr);
}
//========================================================================================
// Write numBytes zeros into the file ref at the current file position.
//========================================================================================
static OSErr WriteZeros(short ref, long numBytes)
{
long big = numBytes / sizeof(long);
long little = numBytes % sizeof(long);
long i, count, zero = 0;
OSErr err = noErr;
// Write long-words first
for (i = 0; i < big && err == noErr; i++) {
count = sizeof(long);
err = FSWrite(ref, &count, &zero);
}
// Now write the rest in bytes
for (i = 0; i < little && err == noErr; i++) {
count = sizeof(char);
err = FSWrite(ref, &count, &zero);
}
return(err);
}
//========================================================================================
// Put a chunk of data into a PICT file (Beaker's buffered version -- 261% faster!)
//========================================================================================
static pascal void PutPICTData(char *buffer, short byteCount)
{
long count, remaining;
LocalRec *theRec;
// Get a pointer to our local data.
theRec = (LocalRec *)GetAGlobal(gExportRef);
// If we get a magic call with byteCount equal to kFlushBufCmd (-1), we know that
// the main loop is trying to flush the writeahead buffer. Special-case this.
if (byteCount == kFlushBufCmd) {
if (theRec->writeUsed > 0) {
count = theRec->writeUsed;
theRec->error = FSWrite(theRec->ref, &count, theRec->writeBuf);
theRec->writeUsed = 0;
}
return;
}
// If an error hasn't occurred, write the bytes QuickDraw is handing us to the output
// file referenced in our local data. Each time we write out some data, bump the byte-
// count so we know exactly how big the picture is.
if (!theRec->error) {
theRec->count += byteCount;
remaining = kWriteBufSize - theRec->writeUsed;
// For big writes, flush buffered data then write the input data in one chunk.
if (byteCount > kWriteBufSize) {
// Write whatever's in the buffer
if (theRec->writeUsed > 0) {
count = theRec->writeUsed;
theRec->error = FSWrite(theRec->ref, &count, theRec->writeBuf);
theRec->writeUsed = 0;
}
// Loop write the input data in one big chunk
if (theRec->error == noErr) {
count = byteCount;
theRec->error = FSWrite(theRec->ref, &count, buffer);
}
} else {
// If the write overflows our buffer, copy the data into our buffer, flush it,
// then buffer the remaining input data.
if (byteCount > remaining) {
BlockMove(buffer, theRec->writeBuf + theRec->writeUsed, remaining);
count = kWriteBufSize;
theRec->error = FSWrite(theRec->ref, &count, theRec->writeBuf);
buffer += remaining;
byteCount -= remaining;
// Buffer the remaining input bytes
if (byteCount > 0) {
BlockMove(buffer, theRec->writeBuf, byteCount);
theRec->writeUsed = byteCount;
} else theRec->writeUsed = 0;
} else {
// This write fits in the buffer, so just buffer it.
BlockMove(buffer, theRec->writeBuf + theRec->writeUsed, byteCount);
theRec->writeUsed += byteCount;
}
}
// Keep the running count in the picture header up to date, too.
if (theRec->thePic)
(*theRec->thePic)->picSize = theRec->count & 0x7FFF;
}
}
//========================================================================================
// Initialize an SBSettingsRec with default values
//========================================================================================
static void InitSettingsRec(SBSettingsPtr settings)
{
settings->hSize = 576; // 8.00 inches (LW letter)
settings->vSize = 776; // 10.78 inches (LW letter)
settings->rows = 2;
settings->columns = 2;
settings->spacing = 10;
}
//========================================================================================
// Shove the values in set into settingsDialog.
//========================================================================================
static void SetToDialog(DialogPtr settingsDialog, SBSettingsPtr set, Boolean redraw)
{
SetIVal(settingsDialog, iHorizSize, set->hSize);
SetIVal(settingsDialog, iVertSize, set->vSize);
SetCValue(settingsDialog, iRowSlider, set->rows);
SetCValue(settingsDialog, iColumnSlider, set->columns);
SetCValue(settingsDialog, iSpacingSlider, set->spacing);
if (redraw) {
DrawNumber(settingsDialog, iRowNumber);
DrawNumber(settingsDialog, iColumnNumber);
DrawNumber(settingsDialog, iSpacingNumber);
DrawExample(settingsDialog, iExample);
}
}
//========================================================================================
// Grab the values in settingsDialog into set.
//========================================================================================
static void DialogToSet(DialogPtr settingsDialog, SBSettingsPtr set)
{
set->hSize = GetIVal(settingsDialog, iHorizSize);
set->vSize = GetIVal(settingsDialog, iVertSize);
set->rows = GetCValue(settingsDialog, iRowSlider);
set->columns = GetCValue(settingsDialog, iColumnSlider);
set->spacing = GetCValue(settingsDialog, iSpacingSlider);
}
//========================================================================================
// Load settings from a file
//========================================================================================
static Boolean LoadSettings(DialogPtr settingsDialog)
{
StandardFileReply reply;
SFTypeList types;
SBSettingsRec loadSet;
GrafPtr oldPort;
long count;
OSErr err = noErr;
short setRef;
Boolean opened = false;
GetPort(&oldPort);
types[0] = kSBSetType;
StandardGetFile(nil, 1, types, &reply);
if (reply.sfGood) {
err = FSpOpenDF(&reply.sfFile, fsWrPerm, &setRef);
if (err == noErr)
opened = true;
if (err == noErr) {
count = sizeof(SBSettingsRec);
err = FSRead(setRef, &count, (Ptr)&loadSet);
}
if (opened)
FSClose(setRef);
}
SetPort(oldPort);
if (reply.sfGood && err == noErr) {
SetToDialog(settingsDialog, &loadSet, kRedraw);
SelIText(settingsDialog, iHorizSize, 0, 32000);
return(true);
} else return(false);
}
//========================================================================================
// Save settings to a file
//========================================================================================
static void SaveSettings(DialogPtr settingsDialog)
{
Str63 prompt, defaultName;
StandardFileReply reply;
SBSettingsRec curSet;
GrafPtr oldPort;
long count;
OSErr err = noErr;
short setRef;
Boolean opened = false;
DialogToSet(settingsDialog, &curSet);
GetPort(&oldPort);
GetIndString(prompt, srStrings, ssSetPrompt);
GetIndString(defaultName, srStrings, ssSetDefaultName);
StandardPutFile(prompt, defaultName, &reply);
if (reply.sfGood) {
FSpDelete(&reply.sfFile);
err = FSpCreate(&reply.sfFile, 'PrMr', kSBSetType, smSystemScript);
if (err == noErr) {
err = FSpOpenDF(&reply.sfFile, fsWrPerm, &setRef);
if (err == noErr)
opened = true;
}
if (err == noErr) {
count = sizeof(SBSettingsRec);
err = FSWrite(setRef, &count, (Ptr)&curSet);
}
if (opened)
FSClose(setRef);
}
SetPort(oldPort);
}
//========================================================================================
// Conduct the settings dialog. Store the result into settings. Returns true if the user
// ended the dialog with OK, false if he Cancelled.
//========================================================================================
static Boolean SettingsDialog(SBSettingsPtr settings, LocalRec *theData)
{
Rect offBox;
DialogPtr settingsDialog;
GrafPtr oldPort;
short item;
Boolean done = false;
// Get and position the dialog
GetPort(&oldPort);
settingsDialog = MyGetNewDialog(srDialog, nil, (WindowPtr)-1);
CenterWindowOnMain(settingsDialog);
SetPort(settingsDialog);
// Set up item values and userItems
SetToDialog(settingsDialog, settings, kNoRedraw);
SetCAction(settingsDialog, iRowSlider, (ProcPtr)RowSliderAction);
SetCAction(settingsDialog, iColumnSlider, (ProcPtr)ColumnSliderAction);
SetCAction(settingsDialog, iSpacingSlider, (ProcPtr)SpacingSliderAction);
UserItem(settingsDialog, iRowNumber, (UserItemProcPtr)DrawNumber);
UserItem(settingsDialog, iColumnNumber, (UserItemProcPtr)DrawNumber);
UserItem(settingsDialog, iSpacingNumber, (UserItemProcPtr)DrawNumber);
UserItem(settingsDialog, iExample, (UserItemProcPtr)DrawExample);
UserItem(settingsDialog, iImageSizeGroup, (UserItemProcPtr)DrawItemFrame);
SelIText(settingsDialog, iHorizSize, 0, 32000);
// Try to make an offscreen buffer
GetDRect(settingsDialog, iExample, &offBox);
if (SafeNewGWorld(&theData->offWorld, 1, &offBox, nil, nil, keepLocal))
LockPixels(theData->offWorld->portPixMap);
// Conduct the dialog
ShowModal(settingsDialog);
do {
PrModalDialog((ModalFilterProcPtr)notextfilter, &item);
switch (item) {
case iOK:
if (!Validate(settingsDialog, iHorizSize, kMinHSize, kMaxHSize) &&
!Validate(settingsDialog, iVertSize, kMinVSize, kMaxVSize)) {
DialogToSet(settingsDialog, settings);
done = true;
} else DrawExample(settingsDialog, iExample);
break;
case iCancel:
done = true;
break;
case iLoad:
LoadSettings(settingsDialog);
break;
case iSave:
SaveSettings(settingsDialog);
break;
case iHorizSize:
case iVertSize:
DrawExample(settingsDialog, iExample);
break;
default:
break;
}
} while (!done);
DisposeModal(settingsDialog);
// Lose the offworld if we made one
if (theData->offWorld != nil) {
DisposeGWorld(theData->offWorld);
theData->offWorld = nil;
}
// Restore the port and return true if the user exited with OK.
SetPort(oldPort);
return(item == iOK);
}
//========================================================================================
// Calculate the parameters for the drawing boxes
//========================================================================================
static void CalcSizes(Rect *srcbox, Rect *dstbox, short rows, short columns,
short spacing, short *hOffset, short *width, short *hSpacing, short *vOffset,
short *height, short *vSpacing)
{
long hRatio, vRatio, theRatio;
short srcWidth, srcHeight, dstWidth, dstHeight;
short usefulH, usefulV;
// Calculate dimensions of the source and destination rectangles.
srcWidth = srcbox->right - srcbox->left;
srcHeight = srcbox->bottom - srcbox->top;
dstWidth = dstbox->right - dstbox->left;
dstHeight = dstbox->bottom - dstbox->top;
// Store horizontal and vertical spacing
*hSpacing = srcWidth * spacing / 100;
*vSpacing = srcHeight * spacing / 100;
// Calculate the horizontal scale-down factor (hRatio, which is a 16.16 fixed-point
// number).
usefulH = (dstWidth - (columns + 1) * (*hSpacing)) / columns;
if (usefulH > srcWidth)
hRatio = 0x00010000;
else hRatio = (usefulH << 16) / srcWidth;
// Calculate the vertical scale-down factor (vRatio, which is a 16.16 fixed-point
// number).
usefulV = (dstHeight - (rows+1) * (*vSpacing)) / rows;
if (usefulV > srcHeight)
vRatio = 0x00010000;
else vRatio = (usefulV << 16) / srcHeight;
// Use the smaller of hRatio or vRatio.
if (hRatio < vRatio)
theRatio = hRatio;
else theRatio = vRatio;
// Calculate and store scaled width and height.
*width = (srcWidth * theRatio) >> 16;
*height = (srcHeight * theRatio) >> 16;
// Calculate and store horizontal and vertical offsets.
*hOffset = (dstWidth - columns * (*width) - (columns - 1) * (*hSpacing)) / 2;
*vOffset = (dstHeight - rows * (*height) - (rows - 1) * (*vSpacing)) / 2;
}
//========================================================================================
// Draw the example box
//========================================================================================
static pascal void DrawExample(WindowPtr theWindow, short item)
{
SBSettingsRec set;
Rect box, hTemp, vTemp, drawBox, srcBox, dstBox, newBox;
GDHandle gd;
GWorldPtr gw;
LocalRec *theData;
short width, height, h, v;
short hOffset, vOffset, hSpacing, vSpacing;
long ratio;
// Get a pointer to our local data
theData = (LocalRec *)GetAGlobal(gExportRef);
// If there's an offworld, use it
GetGWorld(&gw, &gd);
if (theData->offWorld != nil)
SetGWorld(theData->offWorld, nil);
PenNormal(); ForeColor(blackColor);
GetDRect(theWindow, item, &box);
EraseRect(&box);
// Retrieve the settings from the three sliders
DialogToSet(theWindow, &set);
if (set.hSize < kMinHSize) // Pin sizes even if they're bogus
set.hSize = kMinHSize;
if (set.hSize > kMaxHSize)
set.hSize = kMaxHSize;
if (set.vSize < kMinVSize)
set.vSize = kMinVSize;
if (set.vSize > kMaxVSize)
set.vSize = kMaxVSize;
// Get the source (video) and destination (printer page) rectangles.
srcBox = theData->vidRect;
SetRect(&dstBox, 0, 0, set.hSize, set.vSize);
// Call CalcSizes to calculate all the values.
CalcSizes(&srcBox, &dstBox, set.rows, set.columns, set.spacing, &hOffset, &width,
&hSpacing, &vOffset, &height, &vSpacing);
// Calculate the printer page aspect ratio and calculate an appropriate display rect.
ratio = ((dstBox.right - dstBox.left) << 16) / (dstBox.bottom - dstBox.top);
newBox = box;
if (ratio >= 0x00010000) {
// Landscape orientation
newBox.bottom = box.top + ((box.bottom - box.top) << 16) / ratio;
OffsetRect(&newBox, 0, (box.bottom - newBox.bottom) / 2);
} else {
// Portrait orientation
newBox.right = box.left + (((box.bottom - box.top) * ratio) >> 16);
OffsetRect(&newBox, (box.right - newBox.right) / 2, 0);
}
FrameRect(&newBox);
InsetRect(&newBox, 1, 1);
// Draw the frame images in 50% gray
SetRect(&vTemp, dstBox.left + hOffset, dstBox.top + vOffset,
dstBox.left + width + hOffset, dstBox.top + height + vOffset);
PenPat(*GetPattern(srGray));
SetGray(0x7fff);
for (v = 0; v < set.rows; v++) {
hTemp = vTemp;
for (h = 0; h < set.columns; h++) {
drawBox = hTemp;
MapRect(&drawBox, &dstBox, &newBox);
FrameRect(&drawBox);
OffsetRect(&hTemp, hSpacing + width, 0);
}
OffsetRect(&vTemp, 0, vSpacing + height);
}
ForeColor(blackColor); PenNormal();
// Blit back if we're drawing offscreen
if (theData->offWorld != nil) {
SetGWorld(gw, gd);
CopyBits((BitMap *)&theData->offWorld->portPixMap, &theWindow->portBits,
&box, &box, srcCopy, nil);
}
SetGWorld(gw, gd);
}
//========================================================================================
// Draw the value of the control at item - 1 in item's rectangle.
//========================================================================================
static pascal void DrawNumber(WindowPtr theWindow, short item)
{
Str32 str;
Rect box;
FontInfo info;
LocalRec *theData;
short value;
// Get a pointer to our local data
theData = (LocalRec *)GetAGlobal(gExportRef);
GetDRect(theWindow, item, &box);
value = GetCValue(theWindow, item - 1);
NumToString(value, str);
EraseRect(&box);
SetFont(fontGeneva9);
GetFontInfo(&info);
MoveTo(box.left, box.bottom - info.descent);
DrawString(str);
SetFont(fontChicago12);
}
//========================================================================================
// action procedure for the column slider
//========================================================================================
static pascal void ColumnSliderAction(void)
{
GrafPtr thePort;
GetPort(&thePort);
DrawNumber(thePort, iColumnNumber);
DrawExample(thePort, iExample);
}
//========================================================================================
// action procedure for the row slider
//========================================================================================
static pascal void RowSliderAction(void)
{
GrafPtr thePort;
GetPort(&thePort);
DrawNumber(thePort, iRowNumber);
DrawExample(thePort, iExample);
}
//========================================================================================
// action procedure for the spacing slider
//========================================================================================
static pascal void SpacingSliderAction(void)
{
GrafPtr thePort;
GetPort(&thePort);
DrawNumber(thePort, iSpacingNumber);
DrawExample(thePort, iExample);
}